﻿//  Copyright (c) 2010 Ray Liang (http://www.dotnetage.com)
//  Dual licensed under the MIT and GPL licenses:
//  http://www.opensource.org/licenses/mit-license.php
//  http://www.gnu.org/licenses/gpl.html

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Globalization;
using System.IO;
using System.Web.Caching;
using System.Web.Routing;
using Microsoft.Practices.Unity;
using System.Web.Security;
using System.Threading;
using DNA.Mvc.UI;
using System.Web.Configuration;
//using DNA.Mvc.Services.Notification;

namespace DNA.Mvc
{
    public class WebSiteContext : IDisposable
    {
        private HttpContextBase _httpContext;
        private RouteData routeData = null;
        private IDataContext _dataContext;
        private WebPage _page;
        private IEnumerable<Permission> rolesPermissions = null;
        private int[] permHashValues = null;

        //public WebSiteContext(Uri uri)
        //{
        //    var _context = new HttpContext(new System.Web.Hosting.SimpleWorkerRequest(VirtualPathUtility.ToAppRelative(uri.LocalPath).Replace("~/", ""), "", null));
        //    _httpContext = new HttpContextWrapper(_context);

        //    foreach (var route in RouteTable.Routes)
        //    {
        //        routeData = route.GetRouteData(_httpContext);
        //        if (routeData != null)
        //        {
        //            var vpath = route.GetVirtualPath(_httpContext.Request.RequestContext, routeData.Values);
        //            if (vpath != null)
        //            {
        //                RouteData.Route = route;
        //                break;
        //            }
        //        }
        //    }
        //}

        //[InjectionConstructor]
        public WebSiteContext(IDataContext dataContext) : this(System.Web.HttpContext.Current, dataContext) { }

        public WebSiteContext(HttpContext httpContext, IDataContext dataContext) : this(new HttpContextWrapper(httpContext), dataContext) { }

        public WebSiteContext(HttpContextBase httpContext, IDataContext dataContext)
        {
            _httpContext = httpContext;
            _dataContext = dataContext;
        }

        /// <summary>
        ///  Gets the current web context for DNA
        /// </summary>
        public static WebSiteContext Current
        {
            get
            {
                return GetService<WebSiteContext>();
            }
        }

        public static IEnumerable<T> GetServices<T>()
        {
            return DependencyResolver.Current.GetServices<T>();
        }

        public static T GetService<T>()
        {
            return DependencyResolver.Current.GetService<T>();
        }

        #region DAL context properties

        public virtual IDataContext DataContext
        {
            get
            {
                return _dataContext;
            }
        }

        private SiteMapNodeProvider provider;

        public SiteMapNodeProvider SiteMapNodeProvider
        {
            get
            {
                if (provider == null)
                    provider = new SiteMapNodeProvider(this);
                return provider;
            }
        }

        private Web web = null;

        /// <summary>
        /// Gets the current web object of request.
        /// </summary>
        public virtual Web Web
        {
            get
            {
                if (IsNotInstalled) return null;

                if (web == null)
                {
                    if (this.RouteData == null) return null;
                    var webName = "home";
                    if (this.RouteData.Values["website"] != null)
                        webName = this.RouteData.Values["website"].ToString();
                    web = _dataContext.Webs.GetByName(webName);
                }
                return web;
            }
        }

        public virtual Web RootWeb
        {
            get
            {
                if (IsNotInstalled) return null;

                if (Web == null) return null;

                if (Web.IsRoot) return web;

                return _dataContext.Webs.GetByName("home");
            }
        }

        /// <summary>
        /// Gets the current page object of request.
        /// </summary>
        public virtual WebPage Page
        {
            get
            {
                if (IsNotInstalled) return null;

                if (_page == null)
                {
                    if (!_found)
                    {
                        _page = GetCurrentPage();
                        if (_page == null)
                        {
                            var url = HttpContext.Request.Url;
                            var _routeData = FindRoute(url);
                            if (_routeData != null)
                            {
                                _page = FindWebPage(url, routeData);
                            }
                        }
                        _found = true;
                    }
                }
                return _page;
            }
            internal set { _page = value; }
        }
        //internal void ResetNewPage() { _found = false; _page = null; }
        private bool _found = false;

        public bool HasWebPage
        {
            get
            {
                if (IsNotInstalled) return false;

                return Page != null;
            }
        }

        public bool IsAuthorized(Type controllerType, string action)
        {
            if (!HttpContext.Request.IsAuthenticated) return false;

            if (controllerType == null) return false;

            if (string.IsNullOrEmpty(action)) return false;

            if (Roles.IsUserInRole("administrators")) return true;

            if (IsNotInstalled) return false;

            if ((Permissions != null) && (permHashValues != null))
            {

                var code = (controllerType.FullName + "." + action).ToLower().GetHashCode();
                return permHashValues.Contains(code);
            }
            return false;
        }

        public bool IsAuthorized<TController>(string action)
        {
            return IsAuthorized(typeof(TController), action);
        }

        public bool IsAuthorized<TController>(TController controller, string action)
        {
            return IsAuthorized<TController>(action);
        }

        //public bool IsAuthorized(Permission perm, string role)
        //{ 
        //    return han
        //}

        /// <summary>
        /// Return permissions for current user's roles.
        /// </summary>
        public IEnumerable<Permission> Permissions
        {
            get
            {
                if (IsNotInstalled) return null;

                if ((rolesPermissions == null) && (HttpContext.Request.IsAuthenticated))
                {
                    rolesPermissions = DataContext.Permissions.GetUserPermissions(User.Identity.Name);
                    if ((rolesPermissions != null) && (rolesPermissions.Count() > 0))
                        permHashValues = rolesPermissions.Select(p => (p.Controller + "." + p.Action).ToLower().GetHashCode()).ToArray();
                }
                return rolesPermissions;
            }
        }

        #endregion

        #region Http context properties

        internal bool IsNotInstalled
        {
            get
            {
                return string.IsNullOrEmpty(WebConfigurationManager.AppSettings["DnaHash"]);
            }
        }

        /// <summary>
        /// Gets the current httpcontext object
        /// </summary>
        public HttpContextBase HttpContext
        {
            get { return _httpContext; }
        }

        /// <summary>
        /// Gets the current user object.
        /// </summary>
        public System.Security.Principal.IPrincipal User
        {
            get
            {
                return this.HttpContext.User;
            }
        }

        public List<CultureInfo> Cultures
        {
            get
            {
                string key = "Global_Cultures";
                if (HttpContext.Cache[key] == null)
                {
                    string[] files = Directory.GetFiles(HttpContext.Server.MapPath("~/App_GlobalResources/language"));
                    List<CultureInfo> cis = new List<CultureInfo>();
                    cis.Add(CultureInfo.CreateSpecificCulture("en-US"));
                    for (int i = 0; i < files.Length; i++)
                    {
                        if (Path.GetExtension(files[i]).ToLower() == ".resx")
                        {
                            string locale = Path.GetFileNameWithoutExtension(files[i]);
                            if (locale.ToLower() == "language")
                                continue;
                            locale = locale.Replace("language.", "");
                            cis.Add(CultureInfo.CreateSpecificCulture(locale));
                        }
                    }

                    HttpContext.Cache.Add(key, cis, null, DateTime.Now.AddMinutes(10), TimeSpan.Zero, System.Web.Caching.CacheItemPriority.Default, null);
                }
                return (List<CultureInfo>)HttpContext.Cache[key];
            }
        }

        //public List<SelectableNode> Themes
        //{
        //    get
        //    {
        //        return WebSite.Themes;
        //    }
        //}
        public string Language
        {
            get
            {
                if (!HttpContext.Request.IsAuthenticated)
                {
                    var request = HttpContext.Request;
                    string key = request.AnonymousID;
                    HttpCookie cookie = request.Cookies[key];
                    if ((cookie != null) && (cookie.Values.AllKeys.Contains("Language")))
                        return cookie.Values["Language"];
                }
                else
                {
                    if (!string.IsNullOrEmpty(HttpContext.Profile["Language"] as string))
                        return HttpContext.Profile["Language"].ToString();
                }

                if (IsNotInstalled)
                {
                    return HttpContext.Request.UserLanguages.FirstOrDefault();
                }
                else
                    return Web.DefaultLanguage;
            }
        }

        public List<SelectableNode> Languages
        {
            get
            {
                string key = "Global_Languages";
                if (HttpContext.Cache[key] == null)
                {
                    List<SelectableNode> nodes = new List<SelectableNode>();
                    foreach (CultureInfo info in Cultures)
                    {
                        SelectableNode node = new SelectableNode() { Title = info.NativeName, Value = info.Name };
                        node.ImageUrl = "~/Content/Images/flags/" + info.Name + ".gif";
                        nodes.Add(node);
                    }
                    HttpContext.Cache.Add(key, nodes, null, DateTime.Now.AddMinutes(10), TimeSpan.Zero, System.Web.Caching.CacheItemPriority.Default, null);
                }
                return (List<SelectableNode>)HttpContext.Cache[key];
            }
        }

        public List<SelectableNode> TimeZones
        {
            get
            {
                string key = "Global_TimeZones";
                if (HttpContext.Cache[key] == null)
                {
                    System.Collections.ObjectModel.ReadOnlyCollection<TimeZoneInfo> zones = TimeZoneInfo.GetSystemTimeZones();
                    List<SelectableNode> nodes = new List<SelectableNode>();
                    foreach (TimeZoneInfo timeZone in zones)
                    {
                        SelectableNode node = new SelectableNode() { Title = timeZone.DisplayName, Value = timeZone.Id };
                        node.ToolTip = timeZone.StandardName;
                        nodes.Add(node);
                    }
                    HttpContext.Cache.Add(key, nodes, null, DateTime.Now.AddMinutes(10), TimeSpan.Zero, System.Web.Caching.CacheItemPriority.Default, null);
                }
                return (List<SelectableNode>)HttpContext.Cache[key];
            }
        }

        public RouteData RouteData
        {
            get
            {
                if (routeData == null)
                    return this._httpContext.Request.RequestContext.RouteData;
                else
                    return routeData;
            }
        }

        public RequestContext RequestContext
        {
            get
            {
                return this._httpContext.Request.RequestContext;
            }
        }

        public static string GetTemporaryDirectory(HttpContextBase httpContext)
        {
            return httpContext.Server.MapPath("~/Shared/temporary/") + Path.GetRandomFileName().Replace(".", "");
        }

        public string GetTemporaryDirectory()
        {
            return GetTemporaryDirectory(HttpContext);
        }

        /// <summary>
        /// Gets the default uri for current request by current route.
        /// </summary>
        public string GetRouteDefaultUrl(string[] ignoreRouteDataKeys, RouteData routeData)
        {
            string defaultUrl = "";
            var routeDataValues = new RouteValueDictionary(routeData.Values);
            var route = (Route)routeData.Route;

            if (route != null)
            {
                if (ignoreRouteDataKeys != null)
                {
                    //Using default value
                    foreach (string key in ignoreRouteDataKeys)
                    {
                        if (route.Defaults.ContainsKey(key) && (routeDataValues.ContainsKey(key)))
                            routeDataValues[key] = route.Defaults[key];
                    }
                }

                var virtualPathData = route.GetVirtualPath(this.HttpContext.Request.RequestContext, routeDataValues);
                if (virtualPathData != null)
                    defaultUrl = virtualPathData.VirtualPath;
            }

            if (!string.IsNullOrEmpty(defaultUrl))
                defaultUrl = defaultUrl.StartsWith("/") ? ("~" + defaultUrl) : ("~/" + defaultUrl);

            return defaultUrl;
        }

        public bool IsDefaultPageExists(string[] ignoreRouteDataKeys)
        {
            return DataContext.WebPages.Find(GetRouteDefaultUrl(ignoreRouteDataKeys, RouteData)) != null;
        }

        public RouteData FindRoute(Uri uri)
        {
            var innerhttpContext = new HttpContext(new System.Web.Hosting.SimpleWorkerRequest(VirtualPathUtility.ToAppRelative(uri.LocalPath).Replace("~/", ""), "", null));
            var wrapper = new HttpContextWrapper(innerhttpContext);

            foreach (var route in RouteTable.Routes)
            {
                routeData = route.GetRouteData(wrapper);
                if (routeData != null)
                {
                    var vpath = route.GetVirtualPath(wrapper.Request.RequestContext, routeData.Values);
                    if (vpath != null)
                    {
                        return routeData;
                    }
                }
            }
            return null;
        }

        public WebPage FindWebPage(Uri uri, RouteData routeData = null)
        {
            if (Web == null) return null;

            if (routeData == null)
                routeData = RouteData;

            if (DataContext == null) return null;
            var vPath = UrlUtility.ParseVirtualPath(uri, _httpContext.Request.ApplicationPath);
            var _tmp = DataContext.WebPages.Find(vPath);

            if (_tmp == null)
            {
                var sharingPages = DataContext.WebPages.Filter(p => p.IsStatic && p.IsShared);
                foreach (var sp in sharingPages)
                {
                    if (sp.Path.Equals(GetRouteDefaultUrl(sp.IgnoreRouteKeys, routeData), StringComparison.OrdinalIgnoreCase))
                    {
                        _tmp = sp;
                        break;
                    }
                }
            }
            return _tmp;
        }

        private WebPage GetCurrentPage()
        {
            return FindWebPage(HttpContext.Request.Url);
        }

        #endregion

        #region Global settings

        public string ApplicationPath
        {
            get
            {
                var request = _httpContext.Request;
                string url = request.Url.Scheme + "://" + request.Url.Authority;
                if (!request.ApplicationPath.Equals("/"))
                    url += request.ApplicationPath;
                //if (Web != null)
                //    if ((!string.IsNullOrEmpty(Web.Name)) && (!Web.Name.Equals("home", StringComparison.OrdinalIgnoreCase)))
                //        url += "/sites/" + Web.Name;
                return url;
            }
        }

        public bool EnablePersonalWeb
        {
            get
            {
                var epw = System.Web.Configuration.WebConfigurationManager.AppSettings["enablePersonalWeb"];
                if (string.IsNullOrEmpty(epw)) return false;
                var result = false;
                bool.TryParse(epw, out result);
                return result;
            }
        }

        public string Title
        {
            get { return SEOHelper.GetTitle(); }
            set { SEOHelper.SetTitle(value); }
        }

        public string Description
        {
            get { return SEOHelper.GetDescription(); }
            set { SEOHelper.SetDescription(value); }
        }

        public string Keywords
        {
            get { return SEOHelper.GetKeywords(); }
            set { SEOHelper.SetKeywords(value); }
        }

        public void SetCulture(string langName)
        {
            SetCulture(new System.Globalization.CultureInfo(langName));
        }

        public void SetCulture(CultureInfo culture)
        {
            Thread.CurrentThread.CurrentUICulture = culture;
            Thread.CurrentThread.CurrentCulture = culture;
        }

        #endregion

        public void Dispose()
        {
            if (DataContext != null)
                DataContext.Dispose();
            GC.SuppressFinalize(this);
        }
    }

}